#include "napi/native_api.h"
#include <thread>
#include <cstdint>
#include <iostream>

/**
 * 第1步：在C侧实现一（多）个函数
 */
// demo01: 传入参数为简单类型
static napi_value Add(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    napi_value sum;
    napi_create_double(env, value0 + value1, &sum);

    return sum;
}

// Demo02: 传入参数为引用类型
napi_value ProcessPerson(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    if (argc < 1) {
        napi_throw_type_error(env, nullptr, "Wrong number of arguments");
        return nullptr;
    }

    napi_value person = args[0];

    // 获取sayHi方法
    napi_value sayHiFunc;
    napi_status status = napi_get_named_property(env, person, "sayHi", &sayHiFunc);
    if (status != napi_ok) {
        napi_throw_type_error(env, nullptr, "Failed to get 'sayHi' property");
        return nullptr;
    }

    // 检查sayHi是否为函数
    napi_valuetype valuetype;
    status = napi_typeof(env, sayHiFunc, &valuetype);
    if (status != napi_ok || valuetype != napi_function) {
        napi_throw_type_error(env, nullptr, "'sayHi' is not a function");
        return nullptr;
    }

    // 调用sayHi方法
    napi_value result;
    status = napi_call_function(env, person, sayHiFunc, 0, nullptr, &result);
    if (status != napi_ok) {
        napi_throw_type_error(env, nullptr, "Failed to call 'sayHi' function");
        return nullptr;
    }

    // 获取sayHi方法的返回值
    size_t str_size;
    status = napi_get_value_string_utf8(env, result, nullptr, 0, &str_size);
    if (status != napi_ok) {
        napi_throw_type_error(env, nullptr, "Failed to get string size");
        return nullptr;
    }

    std::string str(str_size + 1, 0);
    status = napi_get_value_string_utf8(env, result, &str[0], str_size + 1, &str_size);
    if (status != napi_ok) {
        napi_throw_type_error(env, nullptr, "Failed to get string value");
        return nullptr;
    }

    // 创建返回的JavaScript字符串
    napi_value returnValue;
    status = napi_create_string_utf8(env, str.c_str(), str_size, &returnValue);
    if (status != napi_ok) {
        napi_throw_type_error(env, nullptr, "Failed to create return string");
        return nullptr;
    }

    return returnValue;
}

// Demo03: 传入参数为函数类型
napi_value CallJsFunction(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 1;
    napi_value args[1];

    // 获取传入的参数
    status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to parse arguments");
        return nullptr;
    }

    // 检查参数个数
    if (argc < 1) {
        napi_throw_type_error(env, nullptr, "Wrong number of arguments");
        return nullptr;
    }

    // 获取参数类型
    napi_valuetype valuetype;
    status = napi_typeof(env, args[0], &valuetype);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to get value type");
        return nullptr;
    }

    // 判断是否为函数类型
    if (valuetype != napi_function) {
        napi_throw_type_error(env, nullptr, "Expected a function");
        return nullptr;
    }

    /**
     * 不涉及转换，该napi_value判断为函数后直接用napi_call_function()在C侧调用之即可
     */
    napi_value ArkTS_function = args[0];

    // 准备global对象，后面调用函数时硬性条件，照做即可
    napi_value global, argv[1];
    status = napi_get_global(env, &global); // 获取全局对象
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to get global object");
        return nullptr;
    }

    // 准备ArkTS侧需要的参数（🔥🔥🔥一定要包装成ArkTS侧能使用的napi_value类型！不能直接使用C++的类型！）
    int32_t result_int32_t = 2;
    status = napi_create_int32(env, result_int32_t, &argv[0]);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to create return value");
        return nullptr;
    }

    // 调用 JS 函数
    napi_value result;
    status = napi_call_function(env, global, ArkTS_function, 1, argv, &result);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to call JS function");
        return nullptr;
    }

    // 返回 JS 函数的调用结果
    return result;
}

// Demo04: 传入参数为数组类型 --- 提取数组前两个元素并返回
napi_value ExtractFirstTwoElements(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value result;

    // 获取 JavaScript 传递的参数
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);
    if (argc < 1) {
        napi_throw_type_error(env, NULL, "Expected one argument");
        return NULL;
    }

    // 检查参数是否为数组
    bool isArray;
    napi_status status = napi_is_array(env, args[0], &isArray);
    if (status != napi_ok || !isArray) {
        napi_throw_type_error(env, NULL, "Argument must be an array");
        return NULL;
    }

    // 获取数组长度
    uint32_t length;
    napi_get_array_length(env, args[0], &length);

    // 如果数组长度不足 2，直接返回空数组
    if (length < 2) {
        napi_create_array(env, &result);
        return result;
    }

    // 创建一个新的数组
    napi_create_array_with_length(env, 2, &result);

    // 提取前两个元素
    for (uint32_t i = 0; i < 2; i++) {
        napi_value element;
        napi_get_element(env, args[0], i, &element);
        napi_set_element(env, result, i, element);
    }

    return result;
}

// Demo05: 测试C侧异步处理 --- 延迟5s后向调用方返回一个int值0
/**
 * 异步上下文对象，个人感觉GPT的这种设计方式还是挺好的，把所有的异步相关要使用的数据都放到这个结构体中，
 * 不管是要用Promise对象还是要用附加的数据，都从这个中取即可
 */
struct AsyncContext {
    napi_async_work work; // 异步任务本身
    napi_deferred deferred; // C侧Promise对象
    
    // 可以在这里添加额外数据
};

// 2.2、定义异步要执行的目标任务
void ExecuteAsyncWork(napi_env env, void *data) {
    // 模拟耗时操作，延迟5秒
    std::this_thread::sleep_for(std::chrono::seconds(5));
}

// 2.3、定义异步完成后的回调
void CompleteAsyncWork(napi_env env, napi_status status, void *data) {
    // 将 data 指针强制转换为 AsyncContext 类型的指针。（这里转换成什么类型是自己决定的，自己传入的附加数据是什么类型就转成什么类型）
    AsyncContext *context = static_cast<AsyncContext *>(data);
    napi_value result;
    napi_create_int32(env, 0, &result);
    // Promise-resolve
    napi_resolve_deferred(env, context->deferred, result);
    // 关闭该异步任务
    napi_delete_async_work(env, context->work);
    delete context;
}

napi_value GetIntAsync(napi_env env, napi_callback_info info) {
    AsyncContext *context = new AsyncContext();
    
    // 1、为上下文对象设置Promise对象
    napi_value promise;
    napi_create_promise(env, &context->deferred, &promise);

    // 2、创建异步任务
    // 2.1、创建异步任务名称
    napi_value task_name;
    napi_create_string_utf8(env, "GetIntValueAsync", NAPI_AUTO_LENGTH, &task_name);
    // 2.4、创建异步任务对象并将其设置到上下文对象中
    napi_create_async_work(env, nullptr, task_name, ExecuteAsyncWork, CompleteAsyncWork, context, &context->work);
    
    // 3、执行异步任务
    napi_queue_async_work(env, context->work);

    // 4、返回Promise对象
    return promise;
}

/**
 * 第3步：第2步导出的库中注册第1步的函数，供ArkTS侧调用
 */
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"processPerson", nullptr, ProcessPerson, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"extractFirstTwoElements", nullptr, ExtractFirstTwoElements, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getIntAsync", nullptr, GetIntAsync, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"callJsFunction", nullptr, CallJsFunction, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

/**
 * 第2步：向ArkTS侧导出so库
 */
// 准备模块加载相关信息，将上述Init函数与本模块名等信息记录下来。
static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void *)0),
    .reserved = {0},
};
// 加载so时，该函数会自动被调用，将上述demoModule模块注册到系统中。
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
